﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MvcWiki.Model;
using MvcWiki.Exceptions;
using System.IO;
using System.Xml;
using MvcWiki.Markup;

namespace MvcWiki.Repository.FileSystem
{
    public class WikiNamespaceFileSystemRepository : IWikiNamespaceRepository
    {
        IWikiRepositoryFactory RepositoryFactory;
        IWikiMarkupProvider WikiMarkupProvider;
        IUnitOfWork UnitOfWork;
        string DataPath;
        IFileSystemWrapper FileSystem;

        public WikiNamespaceFileSystemRepository(IWikiRepositoryFactory repositoryFactory, IWikiMarkupProvider wikiMarkupProvider, IUnitOfWork unitOfWork, string dataPath, IFileSystemWrapper fileSystem)
        {
            this.RepositoryFactory = repositoryFactory;
            this.WikiMarkupProvider = wikiMarkupProvider;
            this.UnitOfWork = unitOfWork;
            this.DataPath = dataPath;
            this.FileSystem = fileSystem;
        }

        #region IUnitOfWorkPersistence

        public void InsertEntity(Entity entity)
        {
            var wikiNamespace = entity as WikiNamespace;

            var wikiNamespaceDirectoryNames = FileSystem.GetDirectories(DataPath, wikiNamespace.Name + "---*");

            var wikiNamespaceDirectoryName = Path.Combine(DataPath, wikiNamespace.Name + "---" + wikiNamespace.Id);

            if (wikiNamespaceDirectoryNames.Length > 0)
            {
                wikiNamespaceDirectoryName = wikiNamespaceDirectoryNames[0];
            }
            else
            {
                FileSystem.CreateDirectory(wikiNamespaceDirectoryName);
            }
            
            var wikiNamespaceFileName = Path.Combine(wikiNamespaceDirectoryName, "wikinamespace.xml");

            if (FileSystem.FileExists(wikiNamespaceFileName))
            {
                throw new NamespaceAlreadyExistsException();
            }

            var xmlDocument = new XmlDocument();

            var wikiMarkupXmlNode = xmlDocument.CreateElement("wikimarkup");
            wikiMarkupXmlNode.InnerText = wikiNamespace.Markup.Name;

            var wikiPageXmlNode = xmlDocument.CreateElement("wikinamespace");
            wikiPageXmlNode.AppendChild(wikiMarkupXmlNode);

            xmlDocument.AppendChild(wikiPageXmlNode);

            FileSystem.WriteToFile(wikiNamespaceFileName, xmlDocument.InnerXml);
        }

        public void UpdateEntity(Entity entity)
        {
            var wikiNamespace = entity as WikiNamespace;

            var wikiNamespaceDirectoryNames = FileSystem.GetDirectories(DataPath, "*---" + wikiNamespace.Id);

            if (wikiNamespaceDirectoryNames.Length == 0)
            {
                throw new NamespaceDoesNotExistException();
            }

            var wikiNamespaceDirectoryName = wikiNamespaceDirectoryNames[0];

            var oldWikiNamespaceName = wikiNamespaceDirectoryName.Remove(wikiNamespaceDirectoryName.LastIndexOf("---")).Substring(wikiNamespaceDirectoryName.LastIndexOf('\\') + 1);

            if (wikiNamespace.Name != oldWikiNamespaceName)
            {
                var newWikiNamespaceDirectoryName = Path.Combine(DataPath, wikiNamespace.Name + "---" + wikiNamespace.Id.ToString());

                FileSystem.MoveDirectory(wikiNamespaceDirectoryName, newWikiNamespaceDirectoryName);

                wikiNamespaceDirectoryName = newWikiNamespaceDirectoryName;
            }

            var wikiNamespaceFileName = Path.Combine(wikiNamespaceDirectoryName, "wikinamespace.xml");

            if (!FileSystem.FileExists(wikiNamespaceFileName))
            {
                throw new NamespaceDoesNotExistException();
            }

            var xmlDocument = new XmlDocument();

            var wikiMarkupXmlNode = xmlDocument.CreateElement("wikimarkup");
            wikiMarkupXmlNode.InnerText = wikiNamespace.Markup.Name;

            var wikiPageXmlNode = xmlDocument.CreateElement("wikinamespace");
            wikiPageXmlNode.AppendChild(wikiMarkupXmlNode);

            xmlDocument.AppendChild(wikiPageXmlNode);

            FileSystem.WriteToFile(wikiNamespaceFileName, xmlDocument.InnerXml);
        }

        public void DeleteEntity(Entity entity)
        {
            var wikiNamespace = entity as WikiNamespace;

            var wikiNamespaceDirectoryNames = FileSystem.GetDirectories(DataPath, wikiNamespace.Name + "---*");

            if (wikiNamespaceDirectoryNames.Length == 0)
            {
                throw new NamespaceDoesNotExistException();
            }

            var wikiNamespaceDirectoryName = wikiNamespaceDirectoryNames[0];

            FileSystem.DeleteDirectory(wikiNamespaceDirectoryName);
        }

        #endregion

        public IList<WikiNamespace> GetAllWikiNamespaces()
        {
            var wikiNamespaceDirectoryNames = FileSystem.GetDirectories(DataPath, "*---*");

            var wikiNamespaces = new List<WikiNamespace>();

            foreach (var wikiNamespaceDirectoryName in wikiNamespaceDirectoryNames)
            {
                var wikiNamespaceFileName = Path.Combine(wikiNamespaceDirectoryName, "wikinamespace.xml");

                if (!FileSystem.FileExists(wikiNamespaceFileName))
                {
                    throw new NamespaceDoesNotExistException();
                }

                var wikiNamespace = new WikiNamespace
                {
                    Id = new Guid(wikiNamespaceDirectoryName.Substring(wikiNamespaceDirectoryName.LastIndexOf("---") + 3)),
                    Name = wikiNamespaceDirectoryName.Remove(wikiNamespaceDirectoryName.LastIndexOf("---")).Substring(wikiNamespaceDirectoryName.LastIndexOf('\\') + 1)
                };

                var wikiPageXmlMetaData = FileSystem.ReadFromFile(wikiNamespaceFileName);
                var xmlDocument = new XmlDocument();
                xmlDocument.LoadXml(wikiPageXmlMetaData);

                var wikiMarkupName = xmlDocument.SelectSingleNode("wikinamespace/wikimarkup").InnerText;
                wikiNamespace.Markup = WikiMarkupProvider.CreateWikiMarkup(wikiMarkupName);

                wikiNamespaces.Add(wikiNamespace);
            }

            return wikiNamespaces;
        }

        public WikiNamespace GetWikiNamespace(string wikiNamespaceName)
        {
            var wikiNamespaceDirNames = FileSystem.GetDirectories(DataPath, wikiNamespaceName + "---*");

            if (wikiNamespaceDirNames.Length == 0)
            {
                throw new NamespaceDoesNotExistException();
            }

            var id = new Guid(wikiNamespaceDirNames[0].Substring(wikiNamespaceDirNames[0].LastIndexOf("---") + 3));

            var wikiNamespaceFileName = Path.Combine(wikiNamespaceDirNames[0], "wikinamespace.xml");

            if (!FileSystem.FileExists(wikiNamespaceFileName))
            {
                throw new NamespaceDoesNotExistException();
            }

            var wikiNamespace = new WikiNamespace
            {
                Id = id,
                Name = wikiNamespaceName
            };

            var wikiPageXmlMetaData = FileSystem.ReadFromFile(wikiNamespaceFileName);
            var xmlDocument = new XmlDocument();
            xmlDocument.LoadXml(wikiPageXmlMetaData);

            var wikiMarkupName = xmlDocument.SelectSingleNode("wikinamespace/wikimarkup").InnerText;
            wikiNamespace.Markup = WikiMarkupProvider.CreateWikiMarkup(wikiMarkupName);

            UnitOfWork.Selected(wikiNamespace, this);

            return wikiNamespace;
        }

        public void SaveWikiNamespace(WikiNamespace wikiNamespace)
        {
            UnitOfWork.Saved(wikiNamespace, this);
        }

        public void DeleteWikiNamespace(WikiNamespace wikiNamespace)
        {
            UnitOfWork.Deleted(wikiNamespace, this);
        }
    }
}
